Skip to content

[pull] main from expo:main - #1160

Merged
pull[bot] merged 9 commits into
code:mainfrom
expo:main
Aug 18, 2026
Merged

[pull] main from expo:main#1160
pull[bot] merged 9 commits into
code:mainfrom
expo:main

Conversation

@pull

@pull pull Bot commented Aug 18, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

skylarbarrera and others added 9 commits August 18, 2026 12:08
# Why

Android biometric prompts default to requiring an explicit confirmation
after a successful passive biometric match. Some applications need the
platform-supported implicit-authentication path while retaining
`BIOMETRIC_STRONG`, the existing `CryptoObject`, and authenticated
keystore access.

# How

Adds an Android-only `requireConfirmation` SecureStore option,
defaulting to `true`. The option is applied only when constructing the
current `BiometricPrompt`; it is not persisted and does not change key
aliases, key generation, authentication strength, invalidation, or iOS
behavior. Reads use the stored authentication requirement together with
the current call's confirmation preference.

The native-component fixture exposes the option for both sync and async
reads/writes.

# Test Plan

Automated:

- `pnpm exec turbo build --filter=expo-secure-store`
- `pnpm test` in `packages/expo-secure-store` (Android and iOS; 37 tests
/ 25 snapshots)
- `pnpm typecheck` in `packages/expo-secure-store`
- `pnpm lint` in `packages/expo-secure-store`
- `pnpm exec et check-packages expo-secure-store`
- `./gradlew :expo-secure-store:testDebugUnitTest` in
`apps/bare-expo/android`

Manual fixture coverage is available in the SecureStore native-component
screen: enable authentication and compare `requireConfirmation`
enabled/disabled for sync and async set/get on an Android device with
passive biometrics. Android may override implicit authentication based
on device or policy. On iOS the option is accepted but ignored,
preserving the existing Keychain prompt behavior.

# Checklist

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [x] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [x] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)

Co-authored-by: Brent Vatne <brentvatne@gmail.com>
Co-authored-by: Łukasz Kosmaty <kosmatylukasz@gmail.com>
# Why

There are multiple warnings which pollute the test output in router

# How

1. Solve suspense warnings by using `renderAsync`/`renderHookAsync`
function from `testing-library`
2. Ignore `LogBoxNotificationContainer` warning
3. Add `renderRouterAsync` utility

# Test Plan

CI

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)

---

<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
…48943)

> [!WARNING]
> **Agent-authored and NOT human-reviewed.** An automated `/verify
--fix` run for #48902 wrote this change and checked it in a sandbox; the
reasoning and evidence are in the outcome comment on that issue. Review
it as you would any external contribution.

Requested by @brentvatne · [investigation
run](https://github.com/expo/expo/actions/runs/31815585821) · refs
#48902


## Why

Reported in [#48902](#48902). The
universal `BottomSheet` wraps `children` in a container of its own on
every platform and hardcodes 16 units of padding on it, and
`BottomSheetProps` has no field for that container — so sheet content
can never reach the sheet's edges. No full-bleed row, image, divider or
list separator is possible.

The `modifiers` escape hatch does not give it back. On Android
`modifiers` goes to `ModalBottomSheet`, not to the padded `Column` that
holds the children
([index.android.tsx#L63](https://github.com/expo/expo/blob/bc467bc47f2ae58f54e75caa1363c4b59b3b38a5/packages/expo-ui/src/universal/BottomSheet/index.android.tsx#L63-L63)).
On iOS it lands on the same `Group` but is appended *after* the
hardcoded entry, so a second `padding` stacks onto the first instead of
replacing it
([index.ios.tsx#L28-L38](https://github.com/expo/expo/blob/bc467bc47f2ae58f54e75caa1363c4b59b3b38a5/packages/expo-ui/src/universal/BottomSheet/index.ios.tsx#L28-L38)).
Web never reads `modifiers` at all, and its inner `div` carries
`padding: 16`
([index.tsx#L101-L104](https://github.com/expo/expo/blob/bc467bc47f2ae58f54e75caa1363c4b59b3b38a5/packages/expo-ui/src/universal/BottomSheet/index.tsx#L101-L104)).

## How

Adds one optional prop, `contentPadding?: number | { top?, bottom?,
left?, right? }`, applied to the content container on all three
platforms. A shared `resolveContentPadding` helper resolves it against
the inset each platform applies today, so **when the prop is omitted
nothing changes**: iOS keeps `{ top: 16, leading: 16, trailing: 16 }`,
Android keeps `padding(16, showDragIndicator ? 0 : 16, 16, 0)`, web
keeps `padding: 16`. This is deliberately not a change to any default —
existing sheets render identically, and `contentPadding={0}` is what
unlocks full-bleed content.

Per-edge values follow the universal layer's style-like naming
(`left`/`right`, as `ScrollView` already maps `paddingLeft` →
`leading`), and an edge left out of the object is `0`, so
`contentPadding` fully owns the container's padding rather than merging
with the platform default. That is a deliberate semantic and worth a
reviewer's eye: on web, whose default bottom inset is 16,
`contentPadding={{ left: 0 }}` therefore clears the bottom inset too
(measured, table below).

Docs data was regenerated with `et gdad -p
expo-ui/universal/bottomsheet`.

## Test Plan

Repo tooling, in a full monorepo checkout at `bc467bc` with `pnpm
install`:

```
packages/expo-ui $ pnpm run typecheck   # clean
packages/expo-ui $ pnpm run lint --max-warnings 0   # Found 0 warnings and 0 errors.
packages/expo-ui $ pnpm test            # Test Suites: 28 passed, Tests: 133 passed
```

Behavior, using the reporter's repro
(`kilarsky/expo-ui-bottom-sheet-content-padding-repro`, `@expo/ui`
57.0.10, `expo` 57.0.12) with the change applied to the installed
package and bundled by Metro:

**iOS** — hosted iPhone simulator, Expo Go, SDK 57:

| Arm | Result |
| --- | --- |
| Unpatched, no prop | blue bar inset 16pt from both sheet edges (the
bug) |
| Patched, prop omitted | inset unchanged — no default changed |
| Patched, `contentPadding={0}` | bar spans the sheet edge to edge |
| Patched, `contentPadding={{ top: 8, left: 40, right: 40 }}` | 40pt
side inset, 8pt above the bar |

The "prop omitted" arm is the guard against changing a default, and the
code makes the same point more strongly than a screenshot can: iOS
resolves to `{ top: 16, bottom: 0, leading: 16, trailing: 16 }`, and
`PaddingModifier` already maps the previously-omitted `bottom` to `0`
(`packages/expo-ui/ios/Modifiers/ViewModifierRegistry.swift`), giving
identical `EdgeInsets`; Android's resolved call expands to literally the
previous `padding(16, showDragIndicator ? 0 : 16, 16, 0)`.

**Web** — the same repro served by Metro and measured in headless Chrome
(`getBoundingClientRect` on the bar against the sheet, plus the content
container's computed padding):

| Arm | Content container padding | Bar gap left / right |
| --- | --- | --- |
| Unpatched, no prop | `16px 16px 16px 16px` | 16 / 16 |
| Patched, prop omitted | `16px 16px 16px 16px` | 16 / 16 |
| Patched, `contentPadding={0}` | `0px 0px 0px 0px` | 0 / 0 |
| Patched, `contentPadding={{ left: 0 }}` | `0px 0px 0px 0px` | 0 / 0 |

**Android** — not exercised on a device. An emulator session was started
for this run and never became available, so the Android arm rests on the
shared, unit-tested resolver and on code symmetry with the two arms that
were measured; a reviewer with an Android device should confirm
`contentPadding={0}` and the `showDragIndicator={false}` default there.
Note also that Compose's `Modifier.padding` rejects negative values, so
a negative `contentPadding` — which iOS and web accept — would throw on
Android; the change does not validate it.

## Checklist

- [x] `CHANGELOG.md` entry added.
- [x] Type-checks, lints and tests via the package's own scripts in a
real monorepo checkout.
- [x] Documentation updated (`bottomsheet.mdx` usage section +
regenerated API data).

---------

Co-authored-by: expo-bot <expo-bot@users.noreply.github.com>
Co-authored-by: nishan (o^▽^o) <nishanbende@gmail.com>
# Why

Fingerprint can load different env files for the same project when
`NODE_ENV` changes.

# How

I updated Fingerprint to use the shared API and set the mode to
`development` before loading Expo config and env files.

# Test Plan

Tests and CI checks pass.

# Checklist

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…ameraView (#49028)

# Why

Mounting and unmounting `CameraView` a few times freezes the iOS UI for
tens of seconds. Reported in #48780 with a repro at
https://github.com/nrgbistro/expo-camera-simulator-freeze-repro.

This is a regression from #44159, which replaced the compile-time `#if
!targetEnvironment(simulator)` guards with a runtime device check. Two
paths were left unguarded, and both end up waiting on a capture graph
that cannot start.

When the graph never starts, every later rebuild of it waits out its own
~9s AVFoundation deadline. Nothing detaches the preview layer on
teardown, so `AVCaptureVideoPreviewLayer` does it in `dealloc`, on the
main thread, against a session still marked running. That is the stack
in the report:

```
CA::Transaction::commit
NSKVODeallocate
AVCaptureVideoPreviewLayer dealloc
AVCaptureSession commitConfiguration
AVCaptureSession _buildAndRunGraph
AVRunLoopCondition _waitInMode
```

Several wedged sessions accumulate, and the waits add up into a freeze.

# How

`stopSession()` now stops the session before it removes inputs and
outputs, instead of deconfiguring a live session and stopping it
afterwards. The stop also moved above the device-availability guard,
which previously returned early and left the session running.

`removeFromSuperview()` captures the session manager and the preview
layer strongly, and detaches the layer on the main thread once the
session has stopped. It captured `self` weakly before, so teardown could
be skipped entirely if React Native released the view first.

`updateCameraIsActive()` checks `hasAvailableCameraDevice` before
starting, which is the call site #44159 missed. `startSession()` and
`stopSession()` already had that check.

# Test Plan

Added two native unit tests in `packages/expo-camera/ios/Tests/`, run
with `et native-unit-tests --packages expo-camera`. Both fail on `main`
and pass here. The teardown test measures how long the main thread
blocks while the preview layer is released, which is the symptom users
see.

Measured the freeze itself on a simulator with no capture device, over 8
mount/unmount cycles: 75.5s wall and 71.4s of main-runloop overrun
before, against 4.1s and 18ms after.
We keep receiving feedback messages like "init", "connect", "submit" as
agents and humans test the command structure.

This PR updates submit-expo-feedback to prefer an explicit --message/-m
and keep the positional message argument with a deprecated warning.

It also limits the feedback string to be at least 40 characters so we
reduce as many of these mistake submissions as possible.

Also updates agent-cli-detector to 0.1.6 to detect opencode and muse
code.
…firmationDialog and Popover (#48949)

# Why

Follow-up to #48904, where @brentvatne spotted that `Background`
declared a
`modifiers` prop but never forwarded it. The same bug is in `main` for
four more
components: `Overlay`, `Alert`, `ConfirmationDialog` and `Popover`.
Every
modifier passed to them is silently ignored.

# How

All four destructured `modifiers` out of the props and used it only as
the
source of event listeners – `createViewModifierEventListener` returns
just
`{ onGlobalEvent }`, so the array itself never reached the native view.
They now
pass it on, the same way `Shapes`, `Button` and `List` already do.

The native side needed no changes: their props inherit the `modifiers`
field
from `UIBaseViewProps`, and `ExpoUIView` wraps each view in
`UIBaseView`, which
calls `applyModifiers`.

# Test Plan

Verified in bare-expo on the iOS simulator: temporarily added
`modifiers={[opacity(0.4)]}` to the `Overlay` in the
native-component-list
screen (not part of this PR) and confirmed the whole composition — the
card and
the NEW badge – renders translucent. Before the fix the modifier had no
effect.

`et check-packages @expo/ui` passes.

| Before | After |
| --- | --- |
| <img width="300" alt="Simulator Screenshot - iPhone 17 Pro -
2026-08-15 at 01 58 02"
src="https://github.com/user-attachments/assets/60adc458-64eb-4b8f-b5be-2ef7669c8dce"
/> | <img width="300" alt="Simulator Screenshot - iPhone 17 Pro -
2026-08-15 at 01 57 53"
src="https://github.com/user-attachments/assets/8687cbe0-3fae-4e7b-b491-4ff461f70d46"
/> |



# Checklist

- [x] I added a `changelog.md` entry and rebuilt the package sources
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
- [x] Conforms with the Documentation Writing Style Guide
# Why

`padding()` without arguments applies SwiftUI's system default padding
to all
edges, but specifying any edge dropped the rest to zero. There was no
way to
express "system default on top, 24 points on the sides" — SwiftUI writes
that
as `.padding(.top).padding(.horizontal, 24)`.

# How

Each edge of the modifier now accepts `number | 'auto'`. Values are
decoded by
a `PaddingValue` convertible (`ios/Convertibles/PaddingValue.swift`)
with
`.auto` and `.points` cases; anything that is neither a number nor
`'auto'`
throws with a message naming the accepted values.

`EdgeInsets` can only carry lengths, so edges resolved to `auto` are
collected
into an `Edge.Set` and applied by a second `padding(_:)` call — padding
modifiers add up and the two calls cover disjoint edges. The system
default has
no fixed length (it adapts to size class and Dynamic Type), so it cannot
be
resolved to a number on either side.

Existing behaviour is unchanged: a specific edge still overrides the
shorthand
covering it, unspecified edges still get no padding, and `padding()`
with no
parameters still pads every edge by the system default.

# Test Plan

Added a "Padding" section to the modifiers screen in
native-component-list
covering `padding()`, `padding({ top: 'auto', horizontal: 24 })` and
`padding({ all: 'auto', leading: 0 })`, and verified it in bare-expo on
the iOS
simulator: the auto edges match the system default of the plain
`padding()` row,
while the explicit edges keep their exact lengths.

`et check-packages @expo/ui` passes and the app builds (`BUILD
SUCCEEDED`).

# Checklist

- [x] I added a `changelog.md` entry and rebuilt the package sources
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
- [x] Conforms with the Documentation Writing Style Guide

---------

Co-authored-by: nishan (o^▽^o) <nishanbende@gmail.com>
@pull pull Bot locked and limited conversation to collaborators Aug 18, 2026
@pull pull Bot added the ⤵️ pull label Aug 18, 2026
@pull
pull Bot merged commit 477378a into code:main Aug 18, 2026
23 of 25 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants